package main

/*
我们怎么理解读写锁呢？当有一个goroutine获得写锁定，其它无论是读锁定还是写锁定都将阻塞直到写解锁；
当有一个goroutine获得读锁定，其它读锁定仍然可以继续；当有一个或任意多个读锁定，写锁定将等待所有读锁定解锁之后才能够进行写锁定。
所以说这里的读锁定（RLock）目的其实是告诉写锁定：有很多人正在读取数据，你给我站一边去，等他们读（读解锁）完你再来写（写锁定）。
我们可以将其总结为如下三条：
1. 同时只能有一个goroutine能够获得写锁定
2. 同时可以有任意多个goroutine获得读锁定
3. 同时只能存在写锁定或读锁定（读和写互斥）
所以，RWMutex这个读写锁，该锁可以加多个读锁或者一个写锁，其经常用于读次数远远多于写次数的场景。
读写锁的写锁只能锁定一次，解锁前不能多次锁定，读锁可以多次，但读解锁次数最多只能比读锁次数多一次。
基本遵循两大原则：
1. 可以随便读，多个goroutine同时读
2. 写的时候，啥也不能干，不能读也不能写
读写锁即时针对于读写操作的互斥锁。它与普通的互斥锁最大的不同是，它可以分别针对读操作和写操作进行锁定和解锁。
读写锁遵循的访问控制规则与互斥锁有所不同，在读写锁管辖的范围内，它允许任意个读操作同时进行，但是在同一时刻，他只允许有一个写操作进行。
并且在某一个写操作被进行的过程中，读操作的进行也是不被允许的。也就是说读写锁控制下的多个写操作之间都是互斥的，并且写操作与读操作之间
也都是互斥的。但是，多个读操作之间却不存在互斥关系。
*/
import (
	"fmt"
	"sync"
	"time"
)

// 读写锁
var rw sync.RWMutex
var wg1 sync.WaitGroup

func main() {
	wg1.Add(6)
	go read(0)
	go read(-1)
	go read(-2)
	go write(1)
	go write(2)
	go write(3)
	wg1.Wait()
}

func read(i int) {
	defer rw.RUnlock()
	defer wg1.Done()
	rw.RLock()
	fmt.Println(i, "开始read...")
	time.Sleep(5 * time.Second)
}

func write(i int) {
	defer wg1.Done()
	defer rw.Unlock()
	rw.Lock()
	fmt.Println(i, "开始写...")
	time.Sleep(5 * time.Second)
}
